// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#pragma once

#include <cantor/unresolved/visitor.h>
#include <turbo/utility/status.h>

namespace cantor::unresolved {
    class UnresolvedConstExpr : public UnresolvedVisitor<UnresolvedConstExpr> {
    public:
        static constexpr const cantor::pb::ast::ExprNodeType TYPE = cantor::pb::ast::ExprNodeType::ENT_CONSTANT_EXPR;

        UnresolvedConstExpr(const cantor::pb::ast::ConstantExpr &node) : instance(node) {
        }

        UnresolvedConstExpr(const cantor::pb::ast::DispatchExpr &node) : instance(node.constant_expr()) {
        }

        ~UnresolvedConstExpr() override = default;

        const pb::Location& location_impl() const {
            return  instance.location();
        }

        const pb::Status &error_status_impl() const {
            return  instance.status();
        }

        /// CET_INT
        bool is_integer() const {
            return type() == cantor::pb::ast::ConstantExprType::CET_INT && instance.has_int_value();
        }

        turbo::Result<int64_t> try_integer() const {
            if (type() != cantor::pb::ast::ConstantExprType::CET_INT) {
                return turbo::invalid_argument_error("not an integer, type is:", cantor::pb::ast::ConstantExprType_Name(type()));
            }
            if (!instance.has_int_value()) {
                return turbo::data_loss_error("proto message missing field int_value");
            }
            return instance.int_value();
        }

        int64_t as_integer() const {
            return  instance.int_value();
        }

        /// CET_DOUBLE
        bool is_double() const {
            return type() == cantor::pb::ast::ConstantExprType::CET_DOUBLE && instance.has_double_value();
        }

        turbo::Result<int64_t> try_double() const {
            if (type() != cantor::pb::ast::ConstantExprType::CET_DOUBLE) {
                return turbo::invalid_argument_error("not an double, type is:", cantor::pb::ast::ConstantExprType_Name(type()));
            }
            if (!instance.has_double_value()) {
                return turbo::data_loss_error("proto message missing field double_value");
            }
            return instance.double_value();
        }

        int64_t as_double() const {
            return  instance.double_value();
        }

        // CET_STRING
        bool is_string() const {
            return type() == cantor::pb::ast::ConstantExprType::CET_STRING && instance.has_str_value();
        }

        turbo::Result<std::string_view> try_string() const {
            if (type() != cantor::pb::ast::ConstantExprType::CET_STRING) {
                return turbo::invalid_argument_error("not an string, type is:", cantor::pb::ast::ConstantExprType_Name(type()));
            }
            if (!instance.has_str_value()) {
                return turbo::data_loss_error("proto message missing field str_value");
            }
            return instance.str_value();
        }

        std::string_view as_string() const {
            return  instance.str_value();
        }

        /// CET_BOOL
        bool is_bool() const {
            return type() == cantor::pb::ast::ConstantExprType::CET_BOOL && instance.has_bool_value();
        }

        turbo::Result<bool> try_bool() const {
            if (type() != cantor::pb::ast::ConstantExprType::CET_DOUBLE) {
                return turbo::invalid_argument_error("not an bool, type is:", cantor::pb::ast::ConstantExprType_Name(type()));
            }
            if (!instance.has_bool_value()) {
                return turbo::data_loss_error("proto message missing field bool_value");
            }
            return instance.bool_value();
        }

        int64_t as_bool() const {
            return  instance.bool_value();
        }

        /// CET_NULL
        bool is_null() const {
            return type() == cantor::pb::ast::ConstantExprType::CET_NULL && instance.has_null_value();
        }

        /// CET_PLACE_HOLDER
        bool is_place_holder() const {
            return type() == cantor::pb::ast::ConstantExprType::CET_PLACE_HOLDER && instance.has_holder();
        }


        // CET_HEX
        bool is_hex() const {
            return type() == cantor::pb::ast::ConstantExprType::CET_HEX && instance.has_hex_value() && instance.hex_value().has_value();
        }

        turbo::Result<std::string_view> try_hex() const {
            if (type() != cantor::pb::ast::ConstantExprType::CET_HEX) {
                return turbo::invalid_argument_error("not an hex, type is:", cantor::pb::ast::ConstantExprType_Name(type()));
            }
            if (!instance.has_hex_value()) {
                return turbo::data_loss_error("proto message missing field str_value");
            }
            if (!instance.hex_value().has_value()) {
                return turbo::data_loss_error("proto message missing field hex_value.value");
            }
            return instance.hex_value().value();
        }

        std::string_view as_hex() const {
            return  instance.hex_value().value();
        }

        // CET_BIT
        bool is_bit() const {
            return type() == cantor::pb::ast::ConstantExprType::CET_BIT && instance.has_bit() && instance.bit().has_value();
        }

        turbo::Result<std::string_view> try_bit() const {
            if (type() != cantor::pb::ast::ConstantExprType::CET_HEX) {
                return turbo::invalid_argument_error("not an bit, type is:", cantor::pb::ast::ConstantExprType_Name(type()));
            }
            if (!instance.has_bit()) {
                return turbo::data_loss_error("proto message missing field bit");
            }
            if (!instance.bit().has_value()) {
                return turbo::data_loss_error("proto message missing field bit.value");
            }
            return instance.bit().value();
        }

        std::string_view as_bit() const {
            return  instance.bit().value();
        }

        /// CET_MAXVALUE
        bool is_max_value() const {
            return type() == cantor::pb::ast::ConstantExprType::CET_MAXVALUE && instance.has_max_value();
        }

        /// CET_MINVALUE
        bool is_min_value() const {
            return type() == cantor::pb::ast::ConstantExprType::CET_MINVALUE && instance.has_min_value();
        }

        cantor::pb::ast::ConstantExprType type() const {
            return instance.type();
        }

        const cantor::pb::ast::ConstantExpr &instance;
    };
} // namespace cantor::unresolved
